﻿#pragma once

#include <stdint.h>

#define INIPARSE_WX
#undef INIPARSE_WX

#ifdef INIPARSE_WX
#include <wx/fileconf.h>
#include <wx/wfstream.h>
#else
#include <boost/property_tree/ptree.hpp>    
#include <boost/property_tree/ini_parser.hpp>    
#endif

/*
structures and definition used by export ChangGuangSuo customised bmp file
2015-11-03: Created by Caibing.Zhu
*/

#define  CALC_OFFSET(type, field)	((size_t)&(((type *)0)->field))

#define CHANGGS_FRAME_START	0x7E
#define CHANGGS_FRAME_END	0xE7

typedef enum{
	CHANGGS_DEVICE_TYPE_BASE = 0x00,
	CHANGGS_DEVICE_TYPE_TELEPHOTO = 0X01,
	CHANGGS_DEVICE_TYPE_LARGEFIELD = 0X02,
	CHANGGS_DEVICE_TYPE_MEDIUMWAVE = 0X04,
	CHANGGS_DEVICE_TYPE_LONGWAVE = 0X08,
	CHANGGS_DEVICE_TYPE_MAX
}CHANGGS_DEVICE_TYPE;

#define DEVICE_STATUS_TELEPHOTO		0X05
#define DEVICE_STATUS_LARGEFIELD	0X09
#define DEVICE_STATUS_MEDIUMWAVE	0X06
#define DEVICE_STATUS_LONGWAVE		0X07

#define FRAME_RATE_50HZ				0X00
#define FRAME_RATE_100				0x02
#define FRAME_RATE_25				0X03

#define CALC_FOCAL_LEN(x, a, b, c, d) (((a) * (x) * (x) * (x) + (b) * (x) * (x) + (c) * (x) + (d)) * 100)
#define CALC_TIME(x, y) ((x) * (y))


static const std::string CAMERA_FILE_NAME = "camerainfo.txt";


#pragma pack(push)
#pragma pack(1)

struct CHANG_GS_DATA{
	unsigned char frame_start;
	unsigned char year;
	unsigned char month;
	unsigned char day;
	unsigned long time;
	unsigned char frame_rate;  // 01：200Hz /10：100Hz
	unsigned char camera_type;
	unsigned char decoderA[3];
	unsigned char decoderE[3];
	unsigned short exposure;
	unsigned char integration_time_unit;
	unsigned char storage_cmd;
	unsigned long target_distance;
	unsigned char filters_status;
	unsigned char processer_valid;
	signed	short azimuth_miss_distance;
	signed	short height_miss_distance;
	unsigned char track_method;

#if (CHANGGS_VERSION == 2)  // 40 bit
	
	unsigned char decoderASpeed[3];
	unsigned char decoderESpeed[3];
	uint16_t reserved;
	unsigned char frame_end;

#else  // 36 bit

	unsigned char reserved;
	unsigned char reserved2;
	unsigned char frame_end;
	unsigned char fpga_reserved[2];		//FPGA 增加两个字节
#endif
};

#pragma pack(pop)


class BaseBmpExtensionInfo
{
public:
	BaseBmpExtensionInfo(CHANG_GS_DATA* data){
		device_type = CHANGGS_DEVICE_TYPE_BASE;
		extension.current_time = data->time;
		extension.azimuth_angle = (data->decoderA[2] << 16 | data->decoderA[1] << 8 | data->decoderA[0]) * (0x80000000 / 0x01000000);
		extension.pitch_angle = (data->decoderE[2] << 16 | data->decoderE[1] << 8 | data->decoderE[0]) * (0x80000000 / 0x01000000);
		extension.distance = ((unsigned long long)data->target_distance) * 100;
#ifdef INIPARSE_WX
		wxFileInputStream stream(CAMERA_FILE_NAME);
		if (stream.IsOk())
		{
			config_file = new wxFileConfig(stream, wxConvUTF8);
		}
#else
		boost::property_tree::ini_parser::read_ini(CAMERA_FILE_NAME, pt);
#endif
	}

	~BaseBmpExtensionInfo() 
	{
#ifdef INIPARSE_WX
		if (config_file != 0)
		{
			delete config_file;
		}
#endif
	}

	unsigned char* GetBmpExtension() { return (unsigned char*)&extension; }
	static void Angle_Double_To_DFM(double totaldu, DFMCODE* ac)
	{
		ac->du = (int)totaldu;
		totaldu = (totaldu - ac->du) * 60;
		ac->fen = (int)totaldu;
		totaldu = (totaldu - ac->fen) * 60;
		ac->miao = (int)(totaldu);
		totaldu = (totaldu - ac->miao) * 1000;
		ac->millmiao = (int)(totaldu );
	}

	static void  TranslateMCRecvData(unsigned char* data, DFMCODE* DecoderA, DFMCODE* DecoderE)
	{
		unsigned long A, E;
		A = 0;
		memcpy(&A, data + CALC_OFFSET(CHANG_GS_DATA, decoderA), 3);
		E = 0;
		memcpy(&E, data + CALC_OFFSET(CHANG_GS_DATA, decoderE), 3);

		double A_du, E_du;
		A_du = (double)A*360.0 / pow(2.0, 24.0);
		E_du = (double)E*360.0 / pow(2.0, 24.0);
		Angle_Double_To_DFM(A_du, DecoderA);
		Angle_Double_To_DFM(E_du, DecoderE);
	}

	static void  TranslateAESpeed(unsigned char* data, double* DecoderA, double* DecoderE)
	{

#if (CHANGGS_VERSION == 2)
		int32_t A, E;
		int nFh = 1;

		A = 0;
		memcpy(&A, data + CALC_OFFSET(CHANG_GS_DATA, decoderASpeed), 3);
		nFh = (A  & (0x1 << 23) ? -1 : 1);
		A = (A & 0x7fffff)*nFh;
		E = 0;
		memcpy(&E, data + CALC_OFFSET(CHANG_GS_DATA, decoderESpeed), 3);
		nFh = (E  & (0x1 << 23) ? -1 : 1);
		E = (E & 0x7fffff)*nFh;
		*DecoderA = (double)A*360.0 / pow(2.0, 24.0);
		*DecoderE = (double)E*360.0 / pow(2.0, 24.0);
#endif
	}

	static void  TranslateFocal(unsigned char* data, float* focal)
	{
		int32_t f = 0;
		int nFh = 1;

		memcpy(&f, data + CALC_OFFSET(CHANG_GS_DATA, exposure), 2);
		nFh = (f  & (0x1 << 15) ? -1 : 1);
		f = (f & 0x7fff)*nFh;

		*focal = (float)f*1.6/pow(10, 3);  // um --> mm
	}

	static void CalcAbsoluteTime(unsigned long all, unsigned short* hour, unsigned short* minite, unsigned short* second, unsigned short* millsecond)
	{
		//	unsigned long remain = all;
		*millsecond = all % 10000;
		all /= 10000;
		*second = all % 60;
		all /= 60;
		*minite = all % 60;
		all /= 60;
		*hour = (unsigned short)all;
	}

	static bool GetChannelData(unsigned char* data_source, int channel, int color_depth, CHANG_GS_DATA* channel_data)
	{
		memset(channel_data, 0x00, sizeof(CHANG_GS_DATA));
		if (color_depth == 8)
		{
			memcpy(channel_data, data_source + (channel - 1) * sizeof(CHANG_GS_DATA), sizeof(CHANG_GS_DATA));
			return true;
		}
		else if (color_depth > 8)
		{
			unsigned short* _buffer = (unsigned short*)data_source;
			_buffer += (channel - 1) * sizeof(CHANG_GS_DATA);
			unsigned char* channel_data_buffer = (unsigned char*)channel_data;
			for (int i = 0; i < sizeof(CHANG_GS_DATA); i++)
			{
				channel_data_buffer[i] = (_buffer[i] >> (color_depth - 8)) & 0x00ff;
			}
			return true;
		}
		return false;
	}

	CHANGGS_DEVICE_TYPE device_type;
protected:
	gtBmpExtension extension;
#ifdef INIPARSE_WX
	wxFileConfig*	config_file;
#else
	boost::property_tree::ptree pt;
#endif
};

// new ADD [9/13/2017 xystar]
class BmpExtensionInfo_ChangGSVer2
{
public:
	BmpExtensionInfo_ChangGSVer2(CHANG_GS_DATA* data){
		memset(&extension, 0x00,sizeof(gtBmpExtension));
		extension.device_status = data->camera_type;
		extension.current_time = data->time;  // 0.1ms
		extension.azimuth_angle = (data->decoderA[2] << 16 | data->decoderA[1] << 8 | data->decoderA[0]) * (0x80000000 / 0x01000000);
		extension.pitch_angle = (data->decoderE[2] << 16 | data->decoderE[1] << 8 | data->decoderE[0]) * (0x80000000 / 0x01000000);
		extension.distance = ((unsigned long long)data->target_distance) * 100;  // src 0.1m   dst: 0.001m
		extension.integration_time = data->integration_time_unit * 10;  // ??
		float focal = .0;
		BaseBmpExtensionInfo::TranslateFocal((unsigned char*)data, &focal); // src 1.6um   dst:
		uint32_t focalLen = uint32_t(focal * 100);  // src: 1.6um			dst: 0.01mm
		memcpy(&extension.focal_len, &focalLen, 4);

		switch (data->frame_rate)
		{
		case 0x1:
			extension.fraps = 200 * 100;    // src: hz   dst:0.01hz
			break;
		case 0x10:
			extension.fraps = 100 * 100;
			break;
		default:
			extension.fraps = 0;
			break;
		}
		
		#if (CHANGGS_VERSION == 2)  // 40 bit
			memcpy(extension.reserved,data->decoderASpeed,3);
			memcpy(extension.reserved + 3,data->decoderESpeed,3);
		#endif
		

	}

	~BmpExtensionInfo_ChangGSVer2()
	{
	}

	unsigned char* GetBmpExtension() { return (unsigned char*)&extension; }

protected:

	gtBmpExtension extension;
};

// end add

class TelePhotoBmpExtensionInfo : public BaseBmpExtensionInfo
{
public:
	TelePhotoBmpExtensionInfo(CHANG_GS_DATA* data) : BaseBmpExtensionInfo(data){
		extension.device_status = DEVICE_STATUS_TELEPHOTO;
		extension.fraps = 25 * 100;
		int a = 0, b = 0, c = 0, d = 0,y = 0;

#ifdef INIPARSE_WX
		config_file->SetPath("Telephoto");
		config_file->Read(wxT("_a"), &a);
		config_file->Read(wxT("_b"), &b);
		config_file->Read(wxT("_c"), &c);
		config_file->Read(wxT("_d"), &d);
		config_file->Read(wxT("_y"), &y);
#else
		a = pt.get<int>("Telephoto._a");
		b = pt.get<int>("Telephoto._b");
		c = pt.get<int>("Telephoto._c");
		d = pt.get<int>("Telephoto._d");
		y = pt.get<int>("Telephoto._y");
#endif
		extension.focal_len = CALC_FOCAL_LEN(data->exposure, a, b, c, d);
		extension.integration_time = CALC_TIME(data->integration_time_unit, y);
	}
};

class largeFieldBmpExtensionInfo : public BaseBmpExtensionInfo
{
public:
	largeFieldBmpExtensionInfo(CHANG_GS_DATA* data) : BaseBmpExtensionInfo(data){
		extension.device_status = DEVICE_STATUS_LARGEFIELD;
		extension.fraps = 25 * 100;
		int a = 0, b = 0, c = 0, d = 0, y = 0;

#ifdef INIPARSE_WX
		config_file->SetPath("LargeVisualField");
		config_file->Read(wxT("_a"), &a);
		config_file->Read(wxT("_b"), &b);
		config_file->Read(wxT("_c"), &c);
		config_file->Read(wxT("_d"), &d);
		config_file->Read(wxT("_y"), &y);
#else
		a = pt.get<int>("LargeVisualField._a");
		b = pt.get<int>("LargeVisualField._b");
		c = pt.get<int>("LargeVisualField._c");
		d = pt.get<int>("LargeVisualField._d");
		y = pt.get<int>("LargeVisualField._y");
#endif
		
		extension.focal_len = CALC_FOCAL_LEN(data->exposure, a, b, c, d);
		extension.integration_time = CALC_TIME(data->integration_time_unit, y);
	}
};

class MediumWaveBmpExtensionInfo : public BaseBmpExtensionInfo
{
public:
	MediumWaveBmpExtensionInfo(CHANG_GS_DATA* data) : BaseBmpExtensionInfo(data){
		extension.device_status = DEVICE_STATUS_MEDIUMWAVE;
		switch (data->frame_rate >> 4 & 0x03)
		{
		case FRAME_RATE_50HZ:
			extension.fraps = 50 * 100;
			break;
		case FRAME_RATE_100:
			extension.fraps = 100 * 100;
			break;
		case FRAME_RATE_25:
			extension.fraps = 25 * 100;
			break;
		default:
			break;
		}
		int a = 0, b = 0, c = 0, d = 0, y = 0;

#ifdef INIPARSE_WX
		config_file->SetPath("MediumWave");
		config_file->Read(wxT("_a"), &a);
		config_file->Read(wxT("_b"), &b);
		config_file->Read(wxT("_c"), &c);
		config_file->Read(wxT("_d"), &d);
		config_file->Read(wxT("_y"), &y);
#else
		a = pt.get<int>("MediumWave._a");
		b = pt.get<int>("MediumWave._b");
		c = pt.get<int>("MediumWave._c");
		d = pt.get<int>("MediumWave._d");
		y = pt.get<int>("MediumWave._y");
#endif
		extension.focal_len = CALC_FOCAL_LEN(data->exposure, a, b, c, d);
		extension.integration_time = CALC_TIME(data->integration_time_unit,y);
	}
};

class LongWaveBmpExtensionInfo : public BaseBmpExtensionInfo
{
public:
	LongWaveBmpExtensionInfo(CHANG_GS_DATA* data) : BaseBmpExtensionInfo(data){
		extension.device_status = DEVICE_STATUS_LONGWAVE;
		switch (data->frame_rate >> 6 & 0x03)
		{
		case FRAME_RATE_50HZ:
			extension.fraps = 50 * 100;
			break;
		case FRAME_RATE_100:
			extension.fraps = 100 * 100;
			break;
		case FRAME_RATE_25:
			extension.fraps = 25 * 100;
			break;
		default:
			break;
		}
		int a = 0, b = 0, c = 0, d = 0, y = 0;

#ifdef INIPARSE_WX
		config_file->SetPath("LongWave");
		config_file->Read(wxT("_a"), &a);
		config_file->Read(wxT("_b"), &b);
		config_file->Read(wxT("_c"), &c);
		config_file->Read(wxT("_d"), &d);
		config_file->Read(wxT("_y"), &y);
#else
		a = pt.get<int>("LongWave._a");
		b = pt.get<int>("LongWave._b");
		c = pt.get<int>("LongWave._c");
		d = pt.get<int>("LongWave._d");
		y = pt.get<int>("LongWave._y");
#endif
	
		extension.integration_time = CALC_TIME(data->integration_time_unit, y);
	}
};

//void GetFrapsAndFocal(CHANG_GS_DATA* cgs_data, unsigned long* Fraps, unsigned long* Focal);
//{
//	if (cgs_data->frame_start != CHANGGS_FRAME_START || cgs_data->frame_end != CHANGGS_FRAME_END)
//		return;
//	BaseBmpExtensionInfo* bmpExt = 0;
//	switch (cgs_data->camera_type)
//	{
//	case CHANGGS_DEVICE_TYPE_TELEPHOTO:
//		bmpExt = new TelePhotoBmpExtensionInfo(cgs_data);
//		break;
//	case CHANGGS_DEVICE_TYPE_LARGEFIELD:
//		bmpExt = new largeFieldBmpExtensionInfo(cgs_data);
//		break;
//	case CHANGGS_DEVICE_TYPE_MEDIUMWAVE:
//		bmpExt = new MediumWaveBmpExtensionInfo(cgs_data);
//		break;
//	case CHANGGS_DEVICE_TYPE_LONGWAVE:
//		bmpExt = new LongWaveBmpExtensionInfo(cgs_data);
//		break;
//	default:
//		break;
//	}
//	if (bmpExt == 0)
//		return;
//	*Fraps = ((gtBmpExtension*)bmpExt->GetBmpExtension())->fraps;
//	*Focal = ((gtBmpExtension*)bmpExt->GetBmpExtension())->focal_len;
//	delete bmpExt;
//}

static __inline void GetFrapsAndFocal(CHANG_GS_DATA* cgs_data, unsigned long* Fraps, unsigned long* Focal)
{
	if (cgs_data->frame_start != CHANGGS_FRAME_START || cgs_data->frame_end != CHANGGS_FRAME_END)
		return;

#if (CHANGGS_VERSION == 2)

	BmpExtensionInfo_ChangGSVer2* bmpExt = new BmpExtensionInfo_ChangGSVer2(cgs_data);

#else

	BaseBmpExtensionInfo* bmpExt = 0;
	switch (cgs_data->camera_type)
	{
	case CHANGGS_DEVICE_TYPE_TELEPHOTO:
		bmpExt = new TelePhotoBmpExtensionInfo(cgs_data);
		break;
	case CHANGGS_DEVICE_TYPE_LARGEFIELD:
		bmpExt = new largeFieldBmpExtensionInfo(cgs_data);
		break;
	case CHANGGS_DEVICE_TYPE_MEDIUMWAVE:
		bmpExt = new MediumWaveBmpExtensionInfo(cgs_data);
		break;
	case CHANGGS_DEVICE_TYPE_LONGWAVE:
		bmpExt = new LongWaveBmpExtensionInfo(cgs_data);
		break;
	default:
		break;
	}

#endif

	if (bmpExt == 0)
		return;
	*Fraps = ((gtBmpExtension*)bmpExt->GetBmpExtension())->fraps;
	*Focal = ((gtBmpExtension*)bmpExt->GetBmpExtension())->focal_len;
	delete bmpExt;
}